package com.validator.exception;

import com.validator.response.ApiResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;

/**
 * 全局异常处理程序
 *
 * @author Amadeus
 * @date 2022/07/07
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     *  valid校验错误拦截处理
     *
     * @param exception 错误信息集合
     * @return 错误信息
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public ApiResponse<?> validationBodyException(MethodArgumentNotValidException exception) {

        BindingResult result = exception.getBindingResult();
        StringBuilder msg = new StringBuilder();
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            errors.forEach(p -> {
                FieldError fieldError = (FieldError) p;
                msg.append(fieldError.getDefaultMessage()).append(";");
            });
        }
        return ApiResponse.error(msg.toString());
    }

    /**
     * 参数类型转换错误
     *
     * @param exception 错误
     * @return 错误信息
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public ApiResponse<?> parameterTypeException(HttpMessageConversionException exception){
        logger.error(exception.getCause().getLocalizedMessage());
        return ApiResponse.error("类型转换错误");
    }

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public ApiResponse<Object> runTimeException(HttpServletRequest request, RuntimeException ex) {
        logErrorInfo(request, ex);
        return ApiResponse.error(ex.getMessage());
    }

    private void logErrorInfo(HttpServletRequest httpRequest, Exception ex) {
        String servletPath = httpRequest.getServletPath();
        logger.error("Error Request: " + httpRequest.getMethod() + " " + servletPath);
        String params;
        if (httpRequest.getContentType() == null || !httpRequest.getContentType().contains("x-www-form-urlencoded")) {
            params = getParams(httpRequest);
        } else {
            params = getFormParams(httpRequest);
        }
        logger.error("Params: " + params);
        logger.error("Exception caught by GlobalExceptionHandler:", ex);
    }

    private String getParams(HttpServletRequest httpRequest) {
        StringBuilder builder = new StringBuilder();
        try {
            try (BufferedReader reader = httpRequest.getReader()) {
                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(line);
                }
            } catch (IOException e) {
                logger.error("", e);
            }
        } catch (IllegalStateException e) {
            logger.error(e.getMessage());
        }
        return builder.toString();
    }

    private String getFormParams(HttpServletRequest httpRequest) {
        StringBuilder builder = new StringBuilder();
        httpRequest.getParameterMap().forEach((key, value) -> {
            builder.append(key).append("=");
            if (value.length > 0) {
                builder.append(value[0]).append(" ");
            } else {
                builder.append(" ");
            }
        });
        return builder.toString();
    }


}
